package server

import (
	"bytes"
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"github.com/google/uuid"
	"gorm.io/gorm"
	"log"
	"yk-travel-social/common/cron"
	"yk-travel-social/common/model/es"
	"yk-travel-social/common/model/mysql"
	"yk-travel-social/common/proto/travel"
	"yk-travel-social/pkg"
)

func Login(in *travel.LoginRequest) (*travel.LoginResponse, error) {
	u := mysql.User{}

	User, err := u.ShowUserByMobile(in.Mobile)
	if err != nil {
		return nil, errors.New("查询用户失败")
	}

	if User.ID == 0 {
		return nil, errors.New("没有该用户")
	}

	if User.Password != in.Password {
		return nil, errors.New("密码错误")
	}

	claims := pkg.CustomClaims{
		ID: uint(User.ID),
	}
	token, err := pkg.NewJWT("2209a").CreateToken(claims)
	if err != nil {
		return nil, errors.New("token生成失败")
	}

	return &travel.LoginResponse{Success: token}, nil
}

func Search(in *travel.SearchRequest) (*travel.SearchResponse, error) {
	var buf bytes.Buffer
	query := map[string]interface{}{}
	if in.ScenicSpotName == "" {
		query = map[string]interface{}{
			"query": map[string]interface{}{
				"match_all": map[string]interface{}{},
			},
		}
	} else {
		query = map[string]interface{}{
			"query": map[string]interface{}{
				"match": map[string]interface{}{
					"ScenicSpotName": in.ScenicSpotName,
				},
			},
		}
	}

	if err := json.NewEncoder(&buf).Encode(query); err != nil {
		log.Fatalf("Error encoding query: %s", err)
	}

	// Perform the search request.
	res, err := es.ES.Search(
		es.ES.Search.WithContext(context.Background()),
		es.ES.Search.WithIndex("travel"),
		es.ES.Search.WithBody(&buf),
		es.ES.Search.WithTrackTotalHits(true),
		es.ES.Search.WithPretty(),
	)
	if err != nil {
		log.Fatalf("Error getting response: %s", err)
	}
	defer res.Body.Close()

	r := map[string]interface{}{}
	json.NewDecoder(res.Body).Decode(&r)

	var item []*travel.ScenicSpotItem

	// Print the ID and document source for each hit.
	for _, hit := range r["hits"].(map[string]interface{})["hits"].([]interface{}) {
		source := hit.(map[string]interface{})["_source"].(map[string]interface{})
		item = append(item, &travel.ScenicSpotItem{
			ScenicSpotId:       int64(source["ID"].(float64)),
			ScenicSpotName:     source["ScenicSpotName"].(string),
			ScenicSpotAddress:  source["ScenicSpotAddress"].(string),
			ScenicSpotDescribe: source["ScenicSpotDescribe"].(string),
		})
	}

	return &travel.SearchResponse{ScenicSpotList: item}, nil
}

func ShowInfo(in *travel.ShowInfoRequest) (*travel.ShowInfoResponse, error) {
	s := mysql.ScenicSpot{}

	info, err := s.ShowInfo(in.ScenicSpotId)
	if err != nil {
		return nil, errors.New("查询是失败")
	}

	fmt.Println(info)

	return &travel.ShowInfoResponse{
		ScenicSpotId:       int64(info.ID),
		ScenicSpotName:     info.ScenicSpotName,
		ScenicSpotAddress:  info.ScenicSpotAddress,
		ScenicSpotDescribe: info.ScenicSpotDescribe,
		Fare:               float32(info.Fare),
		Stock:              int64(info.Stock),
		StartTime:          info.StartTime,
		EndTime:            info.EndTime,
		OpeningGarden:      int64(info.OpeningGarden),
	}, nil

}

func BuyTicket(in *travel.BuyTicketRequest) (*travel.BuyTicketResponse, error) {
	s := mysql.ScenicSpot{}
	ScenicSpot, err := s.ShowInfo(in.ScenicSpotId)
	if err != nil {
		return nil, errors.New("查询是失败")
	}

	p := mysql.Purchaser{}
	purchaser, err := p.ShowPurchaser(in.PurchaserId)
	if err != nil {
		return nil, errors.New("查询是失败")
	}

	list, err := p.List()
	if err != nil {
		return nil, errors.New("查询是失败")
	}

	if purchaser.ID == 0 {
		return nil, errors.New("购票人信息不存在")
	}

	if ScenicSpot.Stock < int(in.Num) {
		return nil, errors.New("库存不足")
	}

	fmt.Println(len(list))
	if int(in.Num) != len(list) {
		return nil, errors.New("还缺少购票人信息，购票数量于购票人信息不一致")
	}

	tx := mysql.DB.Begin()

	num := ScenicSpot.Stock - int(in.Num)
	err = s.DedStock(in.ScenicSpotId, num)
	if err != nil {
		tx.Rollback()
		return nil, errors.New("扣减库存失败")
	}

	orderNumber := uuid.New().String()
	o := mysql.Order{
		Model:          gorm.Model{},
		UserId:         int(in.UserId),
		PurchaserId:    int(in.PurchaserId),
		PurchaserName:  purchaser.Name,
		OrderNumber:    orderNumber,
		ScenicSpotId:   int(in.ScenicSpotId),
		ScenicSpotName: ScenicSpot.ScenicSpotName,
		Fare:           ScenicSpot.Fare * float64(in.Num),
		Num:            int(in.Num),
		Insure:         1,
	}
	err = o.CreateOrder()
	if err != nil {
		tx.Rollback()
		return nil, errors.New("订单创建失败")
	}

	tx.Commit()
	sprintf := fmt.Sprintf("%.2f", ScenicSpot.Fare*float64(in.Num))
	alipay := pkg.NewAlipay()
	pay := alipay.Pay(ScenicSpot.ScenicSpotName, orderNumber, sprintf)

	go func() {
		cron.DedOrder(orderNumber, in.ScenicSpotId, in.Num, 3)
	}()

	return &travel.BuyTicketResponse{
		Success: pay,
	}, nil
}

func AddPurchaser(in *travel.AddPurchaserRequest) (*travel.AddPurchaserResponse, error) {

	name := pkg.RealName(in.Name, in.IdCary)
	if !name {
		return nil, fmt.Errorf("实名认证失败")
	}

	p := mysql.Purchaser{
		Model:  gorm.Model{},
		Name:   in.Name,
		IdCary: in.IdCary,
	}
	err := p.CreatePurchaser()
	if err != nil {
		return nil, errors.New("购票人入库失败")
	}

	return &travel.AddPurchaserResponse{Success: "添加购票人成功"}, nil
}

func AlipayBack(in *travel.AlipayBackRequest) (*travel.AlipayBackResponse, error) {

	o := mysql.Order{}

	sn, err := o.ShowOrderByOrderSn(in.Sn)
	if err != nil {
		log.Println("查询失败")
		return nil, err
	}

	if sn.ID == 0 {
		log.Println("没有该订单")
		return nil, err
	}

	err = o.UpdateOrderStatus(in.Sn, in.Status)
	if err != nil {
		log.Println("回调修改状态失败")
		return nil, err
	}

	return &travel.AlipayBackResponse{
		Success: "回调成功",
	}, nil
}

func ShowOrder(in *travel.ShowOrderRequest) (*travel.ShowOrderResponse, error) {
	o := mysql.Order{}

	list, err := o.ShowOrderList(in.UserId)
	if err != nil {
		return nil, errors.New("查询失败")
	}

	var item []*travel.OrderItem

	for _, order := range list {
		item = append(item, &travel.OrderItem{
			OrderId:        int64(order.ID),
			UserId:         int64(order.UserId),
			PurchaserId:    int64(order.PurchaserId),
			PurchaserName:  order.PurchaserName,
			OrderNumber:    order.OrderNumber,
			ScenicSpotId:   int64(order.ScenicSpotId),
			ScenicSpotName: order.ScenicSpotName,
			Fare:           float32(order.Fare),
			Num:            int64(order.Num),
			Insure:         int64(order.Insure),
			OrderStatus:    int64(order.OrderStatus),
		})
	}

	return &travel.ShowOrderResponse{OrderList: item}, nil
}
